package com.ruint.core.utils.volley;

import java.io.File;
import java.io.IOException;
import java.util.Map;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPatch;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.ByteArrayBody;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HTTP;

import com.android.volley.AuthFailureError;
import com.android.volley.Request;
import com.android.volley.Request.Method;
import com.android.volley.toolbox.HurlStack;
import com.ruint.core.utils.preferences.CorePreferences;

/**
 * ruint.core.utils.volley.VolleyUtils
 * 
 * @author ruint <br/>
 *         create at 2014-11-12 上午11:18:38
 */
public class MultipartStack extends HurlStack {
  private static final String TAG = MultipartStack.class.getSimpleName();
  private final static String HEADER_CONTENT_TYPE = "Content-Type";

  @Override
  public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders) throws IOException,
      AuthFailureError {

    if (!(request instanceof MultipartRequest)) {
      return super.performRequest(request, additionalHeaders);
    } else {
      return performMultiPartRequest(request, additionalHeaders);
    }
  }

  private static void addHeaders(HttpUriRequest httpRequest, Map<String, String> headers) {
    for (String key : headers.keySet()) {
      httpRequest.setHeader(key, headers.get(key));
    }
  }

  public HttpResponse performMultiPartRequest(Request<?> request, Map<String, String> additionalHeaders)
      throws IOException, AuthFailureError {
    HttpUriRequest httpRequest = createMultiPartRequest(request, additionalHeaders);
    addHeaders(httpRequest, additionalHeaders);
    addHeaders(httpRequest, request.getHeaders());
    HttpParams httpParams = httpRequest.getParams();
    int timeoutMs = request.getTimeoutMs();

    if (timeoutMs != -1) {
      HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);
    }

    /* Make a thread safe connection manager for the client */
    HttpClient httpClient = new DefaultHttpClient(httpParams);

    return httpClient.execute(httpRequest);
  }

  static HttpUriRequest createMultiPartRequest(Request<?> request, Map<String, String> additionalHeaders)
      throws AuthFailureError {
    switch (request.getMethod()) {
      case Method.DEPRECATED_GET_OR_POST: {
        // This is the deprecated way that needs to be handled for backwards
        // compatibility.
        // If the request's post body is null, then the assumption is that the
        // request is
        // GET. Otherwise, it is assumed that the request is a POST.
        byte[] postBody = request.getBody();
        if (postBody != null) {
          HttpPost postRequest = new HttpPost(request.getUrl());
          if (request.getBodyContentType() != null)
            postRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
          HttpEntity entity;
          entity = new ByteArrayEntity(postBody);
          postRequest.setEntity(entity);
          return postRequest;
        } else {
          return new HttpGet(request.getUrl());
        }
      }
      case Method.GET:
        return new HttpGet(request.getUrl());
      case Method.DELETE:
        return new HttpDelete(request.getUrl());
      case Method.POST: {
        HttpPost postRequest = new HttpPost(request.getUrl());
        if (request.getBodyContentType() != null) {
          postRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
        }
        setMultiPartBody(postRequest, request);
        return postRequest;
      }
      case Method.PUT: {
        HttpPut putRequest = new HttpPut(request.getUrl());
        if (request.getBodyContentType() != null)
          putRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
        setMultiPartBody(putRequest, request);
        return putRequest;
      }
      // Added in source code of Volley libray.
      case Method.PATCH: {
        HttpPatch patchRequest = new HttpPatch(request.getUrl());
        if (request.getBodyContentType() != null)
          patchRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
        return patchRequest;
      }
      default:
        throw new IllegalStateException("Unknown request method.");
    }
  }

  /**
   * If Request is MultiPartRequest type, then set MultipartEntity in the
   * httpRequest object.
   * 
   * @param httpRequest
   * @param request
   * @throws AuthFailureError
   */
  private static void setMultiPartBody(HttpEntityEnclosingRequestBase httpRequest, Request<?> request)
      throws AuthFailureError {

    // Return if Request is not MultiPartRequest
    if (!(request instanceof MultipartRequest)) {
      return;
    }

    // MultipartEntity multipartEntity = new
    // MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);

    MultipartEntityBuilder builder = MultipartEntityBuilder.create();

    /* example for setting a HttpMultipartMode */
    builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);

    // Iterate the fileUploads
    Map<String, File> fileMap = ((MultipartRequest) request).getFile();
    if (fileMap != null) {
      for (Map.Entry<String, File> entry : fileMap.entrySet()) {
        try {
          builder.addPart(((String) entry.getKey()), new FileBody((File) entry.getValue()));
          CorePreferences.DEBUG(TAG + " fileMap :" + (String) entry.getKey() + "," + entry.getValue() == null ? "null"
              : entry.getValue().length() + "");
        } catch (Exception e) {
          CorePreferences.DEBUG(TAG + " fileMap : " + e.getMessage().toString());
        }
      }
    }
    // Iterate the byteArrayBodys
    Map<String, ByteArrayBody> byteMap = ((MultipartRequest) request).getByte();
    if (byteMap != null) {
      for (Map.Entry<String, ByteArrayBody> entry : byteMap.entrySet()) {
        try {
          builder.addPart(((String) entry.getKey()), entry.getValue());
          CorePreferences.DEBUG(TAG + " byteMap :" + (String) entry.getKey() + "," + entry.getValue() == null ? "null"
              : entry.getValue().getContentLength() + "");
        } catch (Exception e) {
          CorePreferences.DEBUG(TAG + " byteMap : " + e.getMessage().toString());
        }
      }
    }
    ContentType contentType = ContentType.create(HTTP.PLAIN_TEXT_TYPE, HTTP.UTF_8);
    // Iterate the stringUploads
    Map<String, String> paramMap = ((MultipartRequest) request).getString();
    for (Map.Entry<String, String> entry : paramMap.entrySet()) {
      try {
        builder.addPart(((String) entry.getKey()), new StringBody((String) entry.getValue(), contentType));
        CorePreferences.DEBUG(TAG + " paramMap :" + (String) entry.getKey() + "," + entry.getValue() == null ? "null"
            : entry.getValue());
      } catch (Exception e) {
        CorePreferences.DEBUG(TAG + " paramMap : " + e.getMessage().toString());
      }
    }

    httpRequest.setEntity(builder.build());
  }
}
